package filetools

import (
	"archive/zip"
	"bufio"
	"bytes"
	"encoding/json"
	"product/common/validator"

	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"sync"
)

var (
	kb = 1024
	mb = kb * 1024
	gb = mb * 1024
	tb = gb * 1024
)

// FileReader 文件读取器
type FileReader struct {
	*bufio.Reader         // 读取器
	file          os.File // 文件
	offSet        int64   // 偏移量
}

// NewFileReader 实例化文件读取器
// filePath 文件路径
// 返回文件读取器实例
func NewFileReader(filePath string) (*FileReader, error) {
	tmpFile, openErr := os.Open(filePath)
	if openErr != nil {
		return nil, openErr
	}
	return &FileReader{
		Reader: bufio.NewReader(tmpFile),
		file:   *tmpFile,
		offSet: 0,
	}, nil
}

// ReadLine 读取一行
// 返回读取的数据和错误
func (f *FileReader) ReadLine() (string, error) {
	readData, readErr := f.Reader.ReadBytes('\n')
	f.offSet += int64(len(readData))
	if readErr != nil || readErr == io.EOF {
		for len(readData) > 0 &&
			(readData[len(readData)-1] == '\r' || readData[len(readData)-1] == '\n') {
			readData = readData[:len(readData)-1]
		}
		return string(readData), readErr
	}
	return "", readErr
}

// OffSet 获取偏移量
// 返回偏移量
func (f *FileReader) OffSet() int64 {
	return f.offSet
}

// SeekOffset 设置偏移量
// offSet 偏移量
func (f *FileReader) SeekOffset(offSet int64) error {
	_, seekErr := f.file.Seek(offSet, 0)
	if seekErr != nil {
		return seekErr
	}
	f.Reader = bufio.NewReader(&f.file)
	f.offSet = offSet
	return nil
}

// Close 关闭文件
// 返回错误
func (f *FileReader) Close() error {
	return f.file.Close()
}

// IsExists 判断文件是否存在
// filePath 文件路径
// 返回是否存在
func IsExists(filePath string) bool {
	_, err := os.Stat(filePath)
	if err == nil {
		return true
	}
	if errors.Is(err, os.ErrNotExist) {
		return false
	}
	return false
}

// CreateFile 创建文件
// filePath 文件路径
func CreateFile(filePath string) bool {
	file, err := os.Create(filePath)
	if err != nil {
		return false
	}
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			fmt.Println(err)
		}
	}(file)
	return true
}

// CreateDir 创建目录
// absPath 目录路径
// 返回错误
func CreateDir(absPath string) error {
	return os.MkdirAll(absPath, os.ModePerm)
}

// CopyDir 复制目录
// srcPath 源目录路径
// dstPath 目标目录路径
// 返回错误
func CopyDir(srcPath string, dstPath string) error {
	srcInfo, err := os.Stat(srcPath)
	if err != nil {
		return fmt.Errorf("failed to get source directory info: %w", err)
	}

	if !srcInfo.IsDir() {
		return fmt.Errorf("source path is not a directory: %s", srcPath)
	}

	err = os.MkdirAll(dstPath, 0755)
	if err != nil {
		return fmt.Errorf("failed to create destination directory: %w", err)
	}

	entries, err := os.ReadDir(srcPath)
	if err != nil {
		return fmt.Errorf("failed to read source directory: %w", err)
	}

	for _, entry := range entries {
		srcDir := filepath.Join(srcPath, entry.Name())
		dstDir := filepath.Join(dstPath, entry.Name())

		if entry.IsDir() {
			err := CopyDir(srcDir, dstDir)
			if err != nil {
				return err
			}
		} else {
			err := CopyFile(srcDir, dstDir)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// IsDir 判断是否是目录
// path 路径
func IsDir(path string) bool {
	file, err := os.Stat(path)
	if err != nil {
		return false
	}
	return file.IsDir()
}

// RemoveFile 删除文件
func RemoveFile(path string) error {
	return os.Remove(path)
}

// CopyFile 复制文件
func CopyFile(srcPath string, dstPath string) error {
	srcFile, err := os.Open(srcPath)
	if err != nil {
		return err
	}
	defer func(srcFile *os.File) {
		_ = srcFile.Close()
	}(srcFile)

	distFile, err := os.Create(dstPath)
	if err != nil {
		return err
	}
	defer func(distFile *os.File) {
		_ = distFile.Close()
	}(distFile)

	var tmp = make([]byte, 1024*4)
	for {
		n, err := srcFile.Read(tmp)
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		_, err = distFile.Write(tmp[:n])
		if err != nil {
			return err
		}
	}
}

// ClearFile 清空文件
func ClearFile(path string) error {
	f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777)
	if err != nil {
		return err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)

	_, err = f.WriteString("")
	return err
}

// ReadFileToString 读取文件为字符串
func ReadFileToString(path string) (string, error) {
	bytes, err := os.ReadFile(path)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

// ReadFileByLine 按行读取文件
func ReadFileByLine(path string) ([]string, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)

	result := make([]string, 0)
	buf := bufio.NewReader(f)

	for {
		line, _, err := buf.ReadLine()
		l := string(line)
		if err == io.EOF {
			break
		}
		if err != nil {
			continue
		}
		result = append(result, l)
	}

	return result, nil
}

// ListFileNames 列出文件名
func ListFileNames(path string) ([]string, error) {
	if !IsExists(path) {
		return []string{}, nil
	}

	fS, err := os.ReadDir(path)
	if err != nil {
		return []string{}, err
	}

	sz := len(fS)
	if sz == 0 {
		return []string{}, nil
	}

	var result []string
	for i := 0; i < sz; i++ {
		if !fS[i].IsDir() {
			result = append(result, fS[i].Name())
		}
	}

	return result, nil
}

// IsZipFile 判断是否是zip文件
func IsZipFile(filepath string) bool {
	f, err := os.Open(filepath)
	if err != nil {
		return false
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)

	buf := make([]byte, 4)
	if n, err := f.Read(buf); err != nil || n < 4 {
		return false
	}

	return bytes.Equal(buf, []byte("PK\x03\x04"))
}

// Zip 压缩文件或文件夹
// path 待压缩的文件或文件夹路径
// destPath 压缩文件保存路径
// 返回错误
func Zip(path string, destPath string) error {
	if IsDir(path) {
		return zipFolder(path, destPath)
	}

	return zipFile(path, destPath)
}

// zipFile 压缩文件
func zipFile(filePath string, destPath string) error {
	zFile, err := os.Create(destPath)
	if err != nil {
		return err
	}
	defer func(zipFile *os.File) {
		_ = zipFile.Close()
	}(zFile)

	archive := zip.NewWriter(zFile)
	defer func(archive *zip.Writer) {
		_ = archive.Close()
	}(archive)

	return addFileToArchive1(filePath, archive)
}

// addFileToArchive1 添加文件到压缩包
func addFileToArchive1(fPath string, archive *zip.Writer) error {
	err := filepath.Walk(fPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		header.Name = strings.TrimPrefix(path, filepath.Dir(fPath)+"/")

		if info.IsDir() {
			header.Name += "/"
		} else {
			header.Method = zip.Deflate
			writer, err := archive.CreateHeader(header)
			if err != nil {
				return err
			}
			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer func(file *os.File) {
				_ = file.Close()
			}(file)
			if _, err := io.Copy(writer, file); err != nil {
				return err
			}
		}
		return nil
	})
	return err
}

// addFileToArchive2
// 递归压缩文件夹
func addFileToArchive2(w *zip.Writer, basePath, baseInZip string) error {
	files, err := os.ReadDir(basePath)
	if err != nil {
		return err
	}
	if !strings.HasSuffix(basePath, "/") {
		basePath = basePath + "/"
	}

	for _, file := range files {
		if !file.IsDir() {
			dat, err := os.ReadFile(basePath + file.Name())
			if err != nil {
				return err
			}

			f, err := w.Create(baseInZip + file.Name())
			if err != nil {
				return err
			}
			_, err = f.Write(dat)
			if err != nil {
				return err
			}
		} else if file.IsDir() {
			newBase := basePath + file.Name() + "/"
			_ = addFileToArchive2(w, newBase, baseInZip+file.Name()+"/")
		}
	}

	return nil
}

// Zip 压缩文件
// folderPath: 文件夹路径
// destPath: 压缩文件保存路径
// 返回值：错误信息
func zipFolder(folderPath string, destPath string) error {
	outFile, err := os.Create(destPath)
	if err != nil {
		return err
	}
	defer func(outFile *os.File) {
		_ = outFile.Close()
	}(outFile)

	w := zip.NewWriter(outFile)

	err = addFileToArchive2(w, folderPath, "")
	if err != nil {
		return err
	}

	err = w.Close()
	if err != nil {
		return err
	}

	return nil
}

// UnZip 解压zip文件
// destPath: 解压到的目标路径
// zipFile: zip文件路径
// 返回值：错误信息
func UnZip(zipFile string, destPath string) error {
	zipReader, err := zip.OpenReader(zipFile)
	if err != nil {
		return err
	}
	defer func(zipReader *zip.ReadCloser) {
		_ = zipReader.Close()
	}(zipReader)

	for _, f := range zipReader.File {
		// issue#62: fix ZipSlip bug
		path, err := safeFilepathJoin(destPath, f.Name)
		if err != nil {
			return err
		}

		if f.FileInfo().IsDir() {
			err = os.MkdirAll(path, os.ModePerm)
			if err != nil {
				return err
			}
		} else {
			err = os.MkdirAll(filepath.Dir(path), os.ModePerm)
			if err != nil {
				return err
			}

			inFile, err := f.Open()
			if err != nil {
				return err
			}
			defer func(inFile io.ReadCloser) {
				_ = inFile.Close()
			}(inFile)

			outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer func(f *os.File) {
				_ = f.Close()
			}(outFile)

			_, err = io.Copy(outFile, inFile)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// ZipAppendEntry 追加文件到zip包
// destPath: zip包路径
// fPath: 要追加的文件路径
func ZipAppendEntry(fPath string, destPath string) error {
	tempFile, err := os.CreateTemp("", "temp.zip")
	if err != nil {
		return err
	}
	defer func(t *os.File) {
		_ = os.Remove(t.Name())
	}(tempFile)

	zipReader, err := zip.OpenReader(destPath)
	if err != nil {
		return err
	}

	archive := zip.NewWriter(tempFile)

	for _, zipItem := range zipReader.File {
		zipItemReader, err := zipItem.Open()
		if err != nil {
			return err
		}
		header, err := zip.FileInfoHeader(zipItem.FileInfo())
		if err != nil {
			return err
		}
		header.Name = zipItem.Name
		targetItem, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(targetItem, zipItemReader)
		if err != nil {
			return err
		}
	}

	err = addFileToArchive1(fPath, archive)

	if err != nil {
		return err
	}

	err = zipReader.Close()
	if err != nil {
		return err
	}
	err = archive.Close()
	if err != nil {
		return err
	}
	err = tempFile.Close()
	if err != nil {
		return err
	}

	return CopyFile(tempFile.Name(), destPath)
}

// IsLink returns true if the given path is a symbolic link.
// path: The path to the file or a file descriptor.
// Returns true if the given path is a symbolic link.
func IsLink(path string) bool {
	fi, err := os.Lstat(path)
	if err != nil {
		return false
	}
	return fi.Mode()&os.ModeSymlink != 0
}

// FileMode returns the file mode of the given file.
// path: The path to the file or a file descriptor.
// Returns the file mode of the file.
func FileMode(path string) (fs.FileMode, error) {
	fi, err := os.Lstat(path)
	if err != nil {
		return 0, err
	}
	return fi.Mode(), nil
}

// MiMeType returns the MIME type of the given file.
// file: The path to the file or a file descriptor.
// Returns the MIME type of the file.
func MiMeType(file any) string {
	var mediaType string

	readBuffer := func(f *os.File) ([]byte, error) {
		buffer := make([]byte, 512)
		_, err := f.Read(buffer)
		if err != nil {
			return nil, err
		}
		return buffer, nil
	}

	if filePath, ok := file.(string); ok {
		f, err := os.Open(filePath)
		if err != nil {
			return mediaType
		}
		buffer, err := readBuffer(f)
		if err != nil {
			return mediaType
		}
		return http.DetectContentType(buffer)
	}

	if f, ok := file.(*os.File); ok {
		buffer, err := readBuffer(f)
		if err != nil {
			return mediaType
		}
		return http.DetectContentType(buffer)
	}
	return mediaType
}

// CurrentPath returns the absolute path of the current directory.
// Returns the absolute path of the current directory.
func CurrentPath() string {
	var absPath string
	_, filename, _, ok := runtime.Caller(1)
	if ok {
		absPath = filepath.Dir(filename)
	}

	return absPath
}

// FileSize returns the size of the given file in bytes.
// path: The path to the file.
// Returns the size of the file in bytes and an error.
func FileSize(path string) (int64, error) {
	f, err := os.Stat(path)
	if err != nil {
		return 0, err
	}
	return f.Size(), nil
}

// DirSize returns the total size of all files in the given directory.
// path: The path to the directory.
// Returns the total size of all files in bytes and an error.
func DirSize(path string) (int64, error) {
	var size int64
	err := filepath.WalkDir(path, func(_ string, d os.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if !d.IsDir() {
			info, err := d.Info()
			if err != nil {
				return err
			}
			size += info.Size()
		}
		return err
	})
	return size, err
}

// ModifyTime returns the modification time of the given file in Unix timestamp format.
// filepath: The path to the file.
// Returns the modification time as an integer representing the number of seconds since the Unix epoch and an error.
func ModifyTime(filepath string) (int64, error) {
	f, err := os.Stat(filepath)
	if err != nil {
		return 0, err
	}
	return f.ModTime().Unix(), nil
}

// Sha returns the SHA-1 hash of the file at the given path.
// filepath: The path to the file.
// shaType: The type of hash algorithm to use. Defaults to SHA-1.
// Returns the SHA hash as a string and an error.
func Sha(filepath string, shaType ...int) (string, error) {
	file, err := os.Open(filepath)
	if err != nil {
		return "", err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(file)

	h := sha1.New()
	if len(shaType) > 0 {
		if shaType[0] == 1 {
			h = sha1.New()
		} else if shaType[0] == 256 {
			h = sha256.New()
		} else if shaType[0] == 512 {
			h = sha512.New()
		} else {
			return "", errors.New("param `shaType` should be 1, 256 or 512")
		}
	}

	_, err = io.Copy(h, file)

	if err != nil {
		return "", err
	}

	sha := fmt.Sprintf("%x", h.Sum(nil))

	return sha, nil

}

// ReadCsvFile reads a CSV file and returns a slice of string slices.
// filepath: The path to the CSV file.
// delimiter: The delimiter character used in the CSV file.
// Returns a slice of string slices representing the parsed CSV data and an error.
func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)

	reader := csv.NewReader(f)
	if len(delimiter) > 0 {
		reader.Comma = delimiter[0]
	}

	records, err := reader.ReadAll()
	if err != nil {
		return nil, err
	}

	return records, nil
}

// WriteCsvFile writes a slice of string slices to a CSV file.
// filepath: The path to the CSV file.
// records: The slice of string slices containing the data to write.
// append: If true, appends the data to the end of the file.
// delimiter: The delimiter character used in the CSV file.
func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error {
	flag := os.O_RDWR | os.O_CREATE

	if append {
		flag = flag | os.O_APPEND
	}

	f, err := os.OpenFile(filepath, flag, 0644)
	if err != nil {
		return err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)
	writer := csv.NewWriter(f)
	// 设置默认分隔符为逗号，除非另外指定
	if len(delimiter) > 0 {
		writer.Comma = delimiter[0]
	} else {
		writer.Comma = ','
	}

	// 遍历所有记录并处理包含分隔符或双引号的单元格
	for i := range records {
		for j := range records[i] {
			records[i][j] = escapeCSVField(records[i][j], writer.Comma)
		}
	}

	return writer.WriteAll(records)
}

// WriteStringToFile writes a string to a file.
// filepath: The path to the file.
// content: The string to write to the file.
// append: If true, appends the content to the end of the file.
func WriteStringToFile(filepath string, content string, append bool) error {
	var flag int
	if append {
		flag = os.O_RDWR | os.O_CREATE | os.O_APPEND
	} else {
		flag = os.O_RDWR | os.O_CREATE | os.O_TRUNC
	}

	f, err := os.OpenFile(filepath, flag, 0644)
	if err != nil {
		return err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)
	//defer f.Close()

	_, err = f.WriteString(content)
	return err
}

// WriteBytesToFile writes bytes to a file.
// It creates the file if it doesn't exist and truncates it if it does.
// filepath: The path to the file.
// content: The bytes to write to the file.
func WriteBytesToFile(filepath string, content []byte) error {
	f, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)
	//defer f.Close()

	_, err = f.Write(content)
	return err
}

// ReadFile reads a file from a given path.
// It supports reading from URLs and local files.
// It returns an io.ReadCloser, a function to close the reader, and an error.
// The caller is responsible for closing the reader.
// path: The path to the file.
// reader: An io.ReadCloser representing the file content.
// closeFn: A function to close the reader.
// err: An error if any occurred during file reading.
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error) {
	switch {
	case validator.IsUrl(path):
		resp, err := http.Get(path)
		if err != nil {
			return nil, func() {}, err
		}
		return resp.Body, func() {
			_ = resp.Body.Close()
		}, nil
	case IsExists(path):
		reader, err := os.Open(path)
		if err != nil {
			return nil, func() {}, err
		}
		return reader, func() { _ = reader.Close() }, nil
	default:
		return nil, func() {}, errors.New("unknown file type")
	}
}

// WriteMapsToCsv writes a slice of maps to a CSV file.
// It accepts a file path, a slice of maps, a boolean indicating whether to append to an existing file,
// and optional delimiter rune.
// filepath: The path to the CSV file.
// records: A slice of maps containing the data to be written.
// appendToExistingFile: A boolean indicating whether to append to an existing file or overwrite.
// delimiter: An optional rune specifying the delimiter character for the CSV file.
// headers: An optional slice of strings specifying the column headers for the CSV file.
// It returns an error if there is an issue writing to the file.
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune,
	headers ...[]string) error {
	for _, record := range records {
		for _, value := range record {
			if !isCsvSupportedType(value) {
				return errors.New("unsupported value type detected; only basic types are supported: \nbool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr")
			}
		}
	}

	var columnHeaders []string
	if len(headers) > 0 {
		columnHeaders = headers[0]
	} else {
		for key := range records[0] {
			columnHeaders = append(columnHeaders, key)
		}
		// sort keys in alphabeta order
		sort.Strings(columnHeaders)
	}

	var dataToWrite [][]string
	if !appendToExistingFile {
		dataToWrite = append(dataToWrite, columnHeaders)
	}

	for _, record := range records {
		var row []string
		for _, h := range columnHeaders {
			row = append(row, fmt.Sprintf("%v", record[h]))
		}
		dataToWrite = append(dataToWrite, row)
	}

	return WriteCsvFile(filepath, dataToWrite, appendToExistingFile, delimiter)
}

// ChunkRead 读取文件
// file 文件对象
// offset 偏移量
// size 读取大小
// bufPool 缓冲区池
// 返回读取的行和错误
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error) {
	buf := bufPool.Get().([]byte)[:size] // 从Pool获取缓冲区并调整大小
	n, err := file.ReadAt(buf, offset)   // 从指定偏移读取数据到缓冲区
	if err != nil && err != io.EOF {
		return nil, err
	}
	buf = buf[:n] // 调整切片以匹配实际读取的字节数

	var lines []string
	var lineStart int
	for i, b := range buf {
		if b == '\n' {
			line := string(buf[lineStart:i]) // 不包括换行符
			lines = append(lines, line)
			lineStart = i + 1 // 设置下一行的开始
		}
	}

	// 处理块末尾的行
	if lineStart < len(buf) {
		line := string(buf[lineStart:])
		lines = append(lines, line)
	}
	// 读取完成后，将缓冲区放回Pool
	bufPool.Put(buf)
	return lines, nil
}

// ParallelChunkRead 并行读取文件
// filePath 文件路径
// linesCh 输出
// chunkSizeMB 块大小，单位MB
// maxGoroutine 最大并发数
// 返回错误
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error {
	if chunkSizeMB == 0 {
		chunkSizeMB = 100
	}
	chunkSize := chunkSizeMB * mb
	// 内存复用
	bufPool := sync.Pool{
		New: func() interface{} {
			return make([]byte, 0, chunkSize)
		},
	}

	if maxGoroutine == 0 {
		maxGoroutine = runtime.NumCPU() // 设置为0时使用CPU核心数
	}

	f, err := os.Open(filePath)
	if err != nil {
		return err
	}

	defer func(f *os.File) {
		_ = f.Close()
	}(f)

	info, err := f.Stat()
	if err != nil {
		return err
	}

	wg := sync.WaitGroup{}
	chunkOffsetCh := make(chan int64, maxGoroutine)

	// 分配工作
	go func() {
		for i := int64(0); i < info.Size(); i += int64(chunkSize) {
			chunkOffsetCh <- i
		}
		close(chunkOffsetCh)
	}()

	// 启动工作协程
	for i := 0; i < maxGoroutine; i++ {
		wg.Add(1)
		go func() {
			for chunkOffset := range chunkOffsetCh {
				chunk, chunkErr := ChunkRead(f, chunkOffset, chunkSize, &bufPool)
				if chunkErr != nil {
					continue
				}
				linesCh <- chunk
			}
			wg.Done()
		}()
	}

	// 等待所有解析完成后关闭行通道
	wg.Wait()
	close(linesCh)
	return nil
}

// isCsvSupportedType 判断是否支持写入CSV
func isCsvSupportedType(v interface{}) bool {
	switch v.(type) {
	case bool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr:
		return true
	default:
		return false
	}
}

// escapeCSVField 处理单元格内容，如果包含分隔符，则用双引号包裹
func escapeCSVField(field string, delimiter rune) string {
	// 替换所有的双引号为两个双引号
	escapedField := strings.ReplaceAll(field, "\"", "\"\"")

	// 如果字段包含分隔符、双引号或换行符，用双引号包裹整个字段
	if strings.ContainsAny(escapedField, string(delimiter)+"\"\n") {
		escapedField = fmt.Sprintf("\"%s\"", escapedField)
	}

	return escapedField
}

// safeFilepathJoin 确保路径安全
func safeFilepathJoin(path1, path2 string) (string, error) {
	relPath, err := filepath.Rel(".", path2)
	if err != nil || strings.HasPrefix(relPath, "..") {
		return "", fmt.Errorf("(zipslip) filepath is unsafe %q: %v", path2, err)
	}
	if path1 == "" {
		path1 = "."
	}
	return filepath.Join(path1, filepath.Join("/", relPath)), nil
}

// PathExists 判断路径是否存在
// path 路径
// 返回是否存在，错误
// 错误为空时，表示不存在
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

// SaveJsonFile 保存json文件
// path 路径
// data 数据
// 返回错误
func SaveJsonFile(path string, data any) error {
	jsonBytes, err := json.Marshal(data)
	if err != nil {
		return err
	}
	return os.WriteFile(path, jsonBytes, 0664)
}

// SaveJsonPretty 保存json文件
// path 路径
// data 数据
// 返回错误
func SaveJsonPretty(path string, data any) error {
	jsonBytes, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(path, jsonBytes, 0664)
}

// ReadJsonFile 读取json文件
// path 路径
// 返回数据，错误
func ReadJsonFile[T any](path string, v *T) error {
	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer func(file *os.File) {
		_ = file.Close()
	}(file)
	return json.NewDecoder(file).Decode(v)
}
